﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using UtilZ.Dotnet.AudioPlayer.Base;
using UtilZ.Dotnet.AudioPlayer.Core;
using UtilZ.Dotnet.AudioPlayer.Native;
using UtilZ.Dotnet.Ex.Audio;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.AudioPlayer.Recorder
{
    /// <summary>
    /// 录音机
    /// </summary>
    unsafe public sealed class SoundRecorder
    {
        /// <summary>
        /// 录音回调
        /// </summary>
        private static readonly RECORDPROC _recordPro;

        /// <summary>
        /// 所有回调对象集合.构造函数添加到集合,Dispose从集合中移除
        /// </summary>
        private static ConcurrentDictionary<int, SoundRecorder> _streamSoundPlayerDic = new ConcurrentDictionary<int, SoundRecorder>();

        static SoundRecorder()
        {
            _recordPro = new RECORDPROC(RecordProCallback);
        }

        private static bool RecordProCallback(int handle, IntPtr buffer, int length, IntPtr user)
        {
            SoundRecorder soundRecorder;
            if (_streamSoundPlayerDic.TryGetValue(handle, out soundRecorder))
            {
                return soundRecorder.RecordDataOutput(buffer, length, user);
            }
            else
            {
                return false;
            }
        }







        private readonly SoundRecorderOptions _options;
        /// <summary>
        /// 获取当前录音选项
        /// </summary>
        public SoundRecorderOptions Options
        {
            get { return this._options; }
        }

        /// <summary>
        /// 录音数据输出委托[第一个参数:当前录音对象;第二个参数:数据]
        /// </summary>
        private readonly Action<SoundRecorder, PtrSpan<byte>> _dataOutput;

        private bool _recording = false;
        /// <summary>
        /// 指示当前是否算在录音标识[true:正在录音;false:已停止录音]
        /// </summary>
        public bool Recording
        {
            get { return this._recording; }
        }

        private int _handle = 0;
        /// <summary>
        /// 获取录音句柄
        /// </summary>
        public int Handle
        {
            get { return this._handle; }
        }


        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="dataOutput">录音数据输出委托[第一个参数:当前录音对象;第二个参数:数据]</param>
        /// <param name="options">录音选项,为null使用默认值选项</param>
        public SoundRecorder(Action<SoundRecorder, PtrSpan<byte>> dataOutput, SoundRecorderOptions options = null)
        {
            if (dataOutput == null)
            {
                throw new ArgumentNullException(nameof(dataOutput));
            }

            if (options == null)
            {
                options = SoundRecorderOptions.Default;
            }

            RecordHelper.RecordInit(options.DeviceId);
            this._options = options;
            this._dataOutput = dataOutput;
        }




        private readonly PtrSpan<byte> _dataBuffer = new PtrSpan<byte>();
        private bool RecordDataOutput(IntPtr buffer, int length, IntPtr user)
        {
            if (length > 0 && buffer != IntPtr.Zero)
            {
                this._dataBuffer.Update((byte*)buffer, length, length);
                this._dataOutput(this, this._dataBuffer);
            }

            return this._recording;
        }







        /// <summary>
        /// 开始录音
        /// </summary>
        public void Start()
        {
            lock (this)
            {
                if (_streamSoundPlayerDic.ContainsKey(this._handle))
                {
                    throw new ApplicationException("对象已在录音");
                }

                BASSFlag flags = this.SampleTypeToBASSFlag(this._options.SampleBits);
                this._recording = true;
                if (this._options.Period > 0)
                {
                    this._handle = RecordHelper.RecordStart(this._options.SampleRate, this._options.Chans, flags, this._options.Period, _recordPro, IntPtr.Zero);
                }
                else
                {
                    this._recording = true;
                    this._handle = RecordHelper.RecordStart(this._options.SampleRate, this._options.Chans, flags, _recordPro, IntPtr.Zero);
                }

                BASSHelper.ChannelPlay(this._handle, false);
                _streamSoundPlayerDic.TryAdd(this._handle, this);
            }
        }

        private BASSFlag SampleTypeToBASSFlag(int sampleBits)
        {
            BASSFlag flags;
            switch (sampleBits)
            {
                case DataSampleBits.BIT8:
                    flags = BASSFlag.BASS_RECORD_PAUSE | BASSFlag.BASS_SAMPLE_8BITS;
                    break;
                case DataSampleBits.BIT16:
                    flags = BASSFlag.BASS_RECORD_PAUSE;
                    break;
                case DataSampleBits.BIT32:
                    flags = BASSFlag.BASS_RECORD_PAUSE | BASSFlag.BASS_SAMPLE_FLOAT;
                    break;
                default:
                    throw new NotImplementedException($"未实现的采样类型:{sampleBits.ToString()}");
            }

            return flags;
        }








        /// <summary>
        /// 停止录音
        /// </summary>
        public void Stop()
        {
            lock (this)
            {
                this._recording = false;
                SoundRecorder soundRecorder;
                _streamSoundPlayerDic.TryRemove(this._handle, out soundRecorder);//构造函数添加到集合,Dispose从集合中移除
                //RecordHelper.RecordPause(this._options.SampleRate, this._options.Chans, _recordPro, IntPtr.Zero);
                this._handle = AudioConstant.NONE_HANDLE;
            }
        }




        /// <summary>
        /// Retrieves information on a recording device
        /// </summary>
        /// <param name="deviceId">The device to get the information of... 0 = first.</param>
        /// <returns>returned BASS_DEVICEINFO_INTERNAL.</returns>
        public static RecordDeviceInfo RecordGetDeviceInfo(int deviceId)
        {
            var deviceInfo = new BASS_DEVICEINFO_INTERNAL();
            if (BASSNativeMethods.BASS_RecordGetDeviceInfo(deviceId, ref deviceInfo))
            {
                return new RecordDeviceInfo(deviceId, ref deviceInfo);
            }

            throw new AudioException(BassErrorCode.GetErrorInfo());
        }

        /// <summary>
        /// 获取当前系统所有的录音设备信息列表
        /// </summary>
        /// <returns>可用的录音设备信息列表</returns>
        public static List<RecordDeviceInfo> GetRecordDeviceInfos()
        {
            //The device to get the information of... 0 = first. 
            var list = new List<RecordDeviceInfo>();
            for (int deviceId = 0; ; deviceId++)
            {
                var info = new BASS_DEVICEINFO_INTERNAL();
                if (BASSNativeMethods.BASS_RecordGetDeviceInfo(deviceId, ref info))
                {
                    list.Add(new RecordDeviceInfo(deviceId, ref info));
                }
                else
                {
                    break;
                }
            }

            return list;
        }

        /// <summary>
        /// Frees all resources used by the recording device. 
        /// </summary>
        public static void Free()
        {
            RecordHelper.RecordFree();
        }
    }
}
